package helpers

import (
	"crypto/md5"
	"fmt"
	"io"
	"math"
	"math/cmplx"
	"unsafe"
)

const m1 = 0x5555555555555555
const m2 = 0x3333333333333333
const m4 = 0x0f0f0f0f0f0f0f0f
const h01 = 0x0101010101010101

// 计算整数换成二进制数后有多少个1,Hamming weight algorithms
func popcnt(x uint64) uint64 {
	x -= (x >> 1) & m1
	x = (x & m2) + ((x >> 2) & m2)
	x = (x + (x >> 4)) & m4
	return (x * h01) >> 56
}

func Str2bytes(s string) []byte {
	x := (*[2]uintptr)(unsafe.Pointer(&s))
	h := [3]uintptr{x[0], x[1], x[1]}
	return *(*[]byte)(unsafe.Pointer(&h))
}

func Bytes2str(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

func Md5V3(str string) string {
	w := md5.New()
	io.WriteString(w, str)
	md5str := fmt.Sprintf("%x", w.Sum(nil))
	return md5str
}

// Newton's method cube root function that hopes for
// convergence within 20 iterations
// 开三次方
func Cbrt1(x complex128) complex128 {
	var z complex128 = x
	for i := 0; i < 20; i++ {
		z = z - ((z*z*z - x) / (3.0 * z * z))
	}
	return z
}

// Newton's method cube root function that runs until stable
func Cbrt(x complex128) complex128 {
	var z, z0 complex128 = x, x
	for {
		z = z - ((z*z*z - x) / (3.0 * z * z))
		if cmplx.Abs(z-z0) < 1e-10 {
			break
		}
		z0 = z
	}
	return z
}

func ExtractRoot(who, num float64) float64 {
	return math.Pow(who, 1.0/num)
}
